<!--suppress VueUnrecognizedSlot, JSStringConcatenationToES6Template, SpellCheckingInspection -->
<!--使用前端虚拟滚动的表格, 好处是可以使用内置的全局搜索, 排序等功能,不用自己写方法, 可以在数据量低于5000的表格使用, 常用于如我的待办等筛选过后的数据-->
<template lang="pug">
q-table(
  :title="title"
  :columns="columns"
  ref="table" dense separator="cell"
  virtual-scroll
  :filter="search_text_trim"
  :style="tableStyle"
  :rows="rows"
  :rows-per-page-options="rows_per_page_options"
  table-header-style="position: sticky; top: 0; z-index: 2;"
  table-header-class="bg-blue-1 text-subtitle"
  v-model:selected="selected"
  v-model:pagination="initial_page_config"
  row-key="id")
  // 自定义加载中样式
  template(v-if="rows?.length>0" v-slot:loading)
    q-inner-loading(showing color="primary" class="z-top")
      q-spinner-hourglass(color="green-6" size="2em")
      div(class="text-green-6") 加载中...
  // 自定义右上角配置
  template(v-slot:top-right="props")
    slot(name="top-right-extra" :props="props")
    // 模版
    q-btn(color="primary" class="q-mx-xs" v-if="canDownloadTemplate" dense label="下载模版"
      @click="downloadTemplate" v-ripple)
    // 删除选中行
    q-btn(color="negative"  class="q-mx-xs" v-if="canDelete && !readOnly" dense label="删除选中行"
      @click="deleteSelected" v-ripple)
    // 导入导出
    common-uploader(v-model="importFileData"
      ref="importFile"
      v-show="false"
      :max-file-size="uploadLimit.maxFileSize"
      :accept="uploadLimit.accept"
      :xq_number="uploadParams.xq_number"
      :ticket_id="uploadParams.ticket_id"
      :state_id="uploadParams.state_id"
      :dir_type="uploadParams.dir_type"
      max-files="1"
      is-import
      @after-uploaded="fileAdded")
    q-btn(color="primary" class="q-mx-xs" v-if="canImport && !readOnly" dense label="导入" @click="importTable"  v-ripple)
    // 导出选中行
    q-btn(color="primary" class="q-mx-xs" v-if="canExport && table?.filteredSortedRows?.length > 0" dense @click="exportTable(selected)"
      label="导出选择行" v-ripple)
    q-btn(color="primary" class="q-mx-xs" v-if="canExport  &&  table?.filteredSortedRows?.length > 0" dense label="导出"
      @click="exportTable(table.filteredSortedRows)" v-ripple)
    // 新增一行
    q-btn(color="primary" class="q-mx-xs" v-if="canAdd && !readOnly" dense label="新增" @click="addRow" v-ripple)
    //全局搜索
    q-input(v-model="search_text" v-if="canSearch" class="q-mx-md" dense debounce="500" placeholder="全局搜索")
      template(v-slot:append)
        q-icon(name="search")
    // 专注模式
    q-btn(flat round dense :icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
      @click="toggleFullscreen(props)"  v-if="fullScreen")
      q-tooltip {{ props.inFullscreen ? '退出专注模式' : '专注模式' }}
  // 表头
  template(v-slot:header-cell="props")
    q-th(:key="props.col.name" v-if="props.col.type!='buttons'" class="bg-blue-1 text-subtitle" style="position: sticky; top: 0; z-index: 2;")
      div(class="full-width text-center")
        span(v-if="!props.col.not_required && !readOnly && props.col.type!='buttons'" class="text-red-6") *
        span {{ props.col.label }}
        q-icon(v-if="props.col.editable" name="r_border_color" class="q-ml-sm q-mb-xs" color="primary" )
    q-th(:key="props.col.name"  v-else class="bg-blue-1 text-subtitle" style="position: sticky; top: 0; right: 0;z-index: 2;")
      div(class="full-width text-center"  style="position:sticky; right: 0; z-index: 1;")
        span {{ props.col.label }}
  // 普通布局
  template(v-slot:body-cell="props")
    q-td(:key="props.col.field" :props="props" class="bg-white" v-if="props.col.type!='buttons'"
      @click="cellClick(props)"
      :style="'border-left: 1px solid rgba(0,0,0,0.14);'+props.col.style?props.col.style:''")
      q-field(v-model="props.value" v-if="!props.col.not_required && !readOnly"
        hide-bottom-space dense borderless  class="full-width"
        :disable="cellDisable(props)"
        :rules="[val=> !!val || is.number(val) || '不能为空']"
        lazy-rules)
        template(v-slot:control)
          div(class="full-width text-center ellipsis-3-lines" style="white-space: normal;") {{ props.value }}
            q-tooltip(max-width="50vw" v-if="props.col.tooltip") {{ props.value }}
          q-popup-edit(v-model="props.row[props.col.field]" :title="`编辑 ${props.col.label}`" @before-show="beforeShow(props)"
            @before-hide="beforeHide(props)"
            @save="saveData(props)"  buttons auto-save v-slot="scope" v-if="!!props.col.editable && !readOnly" label-set="确认" )
            q-select(v-model="scope.value" v-if="props.col.type=='select'" @keyup.enter="scope.set"
              dense :options="props.col.options")
            q-option-group(v-else-if="props.col.type=='radio'" v-model="scope.value" :options="props.col.options"
              @keyup.enter="scope.set" @update:model-value="scope.set"
              inline)
            time-range(v-else-if="props.col.type=='time_range'" v-model="scope.value" @keyup.enter="scope.set")
            normal-number-input(v-model="scope.value"  v-else-if="props.col.type=='number'" @keyup.enter="scope.set"
              :dig-num="props.col.dig_num" fill-mask="0"  autofocus
            dense hide-bottom-space filled clearable)
            normal-number-input(v-model="scope.value"  v-else-if="props.col.type=='money'" @keyup.enter="scope.set"
              :dig-num="2" fill-mask="0"  autofocus
            dense hide-bottom-space filled clearable)
            normal-number-input(v-model="scope.value"  v-else-if="props.col.type=='normal_number'" @keyup.enter="scope.set"
              :dig-num="props.col.dig_num" autofocus
            dense hide-bottom-space filled clearable)
            div(v-else-if="props.col.type=='datetime'" class="q-gutter-xs  row items-start")
              q-date(v-model="scope.value" mask="YYYY-MM-DD HH:mm:ss"  @keyup.enter="scope.set")
              q-time(v-model="scope.value" mask="YYYY-MM-DD HH:mm:ss" format24h  @keyup.enter="scope.set")
            date-range(v-else-if="props.col.type=='date'" v-model="scope.value" @keyup.enter="scope.set" @update:model-value="scope.set"
              :date-format="props.col.date_options.format"
              :exclude-date="props.col.date_options.exclude_date"
              :start-time="dateRangeStartTime(props)"
              :end-time="dateRangeEndTime(props)")
            opt-select(v-model="scope.value" v-else-if="props.col.type=='map-select'" @keyup.enter="scope.set"
              filterable :filter-function="props.col.filterFunction"
              transition-show="scale" transition-hide="scale"
              dense hide-bottom-space clearable
              filled  options-dense
              :options="props.col.options")
            opt-select(v-model="scope.value" v-else-if="props.col.type=='user-select'" @keyup.enter="scope.set"
              filterable
              transition-show="scale" transition-hide="scale"
              dense hide-bottom-space clearable
              filled  options-dense
              :options="userList")
            opt-select(v-model="scope.value" v-else-if="props.col.type=='system-select'"  @keyup.enter="scope.set"
              filterable
              transition-show="scale" transition-hide="scale"
              dense hide-bottom-space clearable
              filled  options-dense
              :options="systemList")
            q-input(v-model="scope.value" v-else-if="props.col.type=='text-area'" @keyup.enter="scope.set" type="textarea" :maxlength="props.col.maxlength" input-style="width:500px"
              dense autofocus counter)
            q-input(v-model="scope.value" v-else @keyup.enter="scope.set" :maxlength="props.col.maxlength"
              dense autofocus counter)
      template(v-else)
        div(class="full-width text-center ellipsis-3-lines" style="white-space: normal;") {{ props.value }}
          q-tooltip(max-width="50vw" v-if="props.col.tooltip") {{ props.value }}
        q-popup-edit(v-model="props.row[props.col.field]" :title="`编辑 ${props.col.label}`" @save="saveData(props)"
          buttons auto-save v-slot="scope" v-if="!!props.col.editable && !readOnly" label-set="确认" )
          q-select(v-model="scope.value" v-if="props.col.type=='select'" @keyup.enter="scope.set"
            dense :options="props.col.options")
          time-range(v-else-if="props.col.type=='time_range'" v-model="scope.value" @keyup.enter="scope.set")
          normal-number-input(v-model="scope.value"  v-else-if="props.col.type=='money'" @keyup.enter="scope.set"
            :dig-num="2" fill-mask="0"  autofocus
          dense hide-bottom-space filled clearable)
          normal-number-input(v-model="scope.value"  v-else-if="props.col.type=='normal_number'" @keyup.enter="scope.set"
            :dig-num="props.col.dig_num" autofocus
          dense hide-bottom-space filled clearable)
          q-option-group(v-else-if="props.col.type=='radio'"  @keyup.enter="scope.set" @update:model-value="scope.set"
            v-model="scope.value" :options="props.col.options" inline)
          div(v-else-if="props.col.type=='datetime'")
            q-date(v-model="scope.value" mask="YYYY-MM-DD HH:mm:ss" @keyup.enter="scope.set")
            q-time(v-model="scope.value" mask="YYYY-MM-DD HH:mm:ss" format24h  @keyup.enter="scope.set")
          date-range(v-else-if="props.col.type=='date'" v-model="scope.value" @keyup.enter="scope.set" @update:model-value="scope.set"
            :date-format="props.col.date_options.format"
            :exclude-date="props.col.date_options.exclude_date"
            :start-time="props.col.date_options.hasOwnProperty('start_time') ? props.col.date_options.start_time : props.row[props.col.date_options.start_time_field]"
            :end-time="dateRangeEndTime(props)")
          opt-select(v-model="scope.value" v-else-if="props.col.type=='map-select'" @keyup.enter="scope.set"
            filterable
            transition-show="scale" transition-hide="scale"
            dense hide-bottom-space clearable
            filled  options-dense :filter-function="props.col.filterFunction"
            :options="props.col.options")
          opt-select(v-model="scope.value" v-else-if="props.col.type=='user-select'" @keyup.enter="scope.set"
            filterable
            transition-show="scale" transition-hide="scale"
            dense hide-bottom-space clearable
            filled  options-dense
            :options="userList")
          opt-select(v-model="scope.value" v-else-if="props.col.type=='system-select'" @keyup.enter="scope.set"
            filterable
            transition-show="scale" transition-hide="scale"
            dense hide-bottom-space clearable
            filled  options-dense
            :options="systemList")
          q-input(v-model="scope.value" v-else-if="props.col.type=='text-area'" @keyup.enter="scope.set" :maxlength="props.col.maxlength" input-style="width:500px"
            dense autofocus counter)
          q-input(v-model="scope.value" v-else @keyup.enter="scope.set" :maxlength="props.col.maxlength"
            dense autofocus counter)
    q-td(:props="props" class="bg-white" v-else-if="props.col.type=='buttons'"
      :style="'background: white; min-width: 100px; max-width: 200px;'+props.col.style?props.col.style:''")
      slot(:name="props.col.field" :props="props")


</template>

<script setup>
import {computed, inject, ref, onMounted} from "vue";
import {useQuasar, debounce, exportFile, is, date} from "quasar";
import TimeRange from "~/components/optimaztion/TimeRange.vue";
import OptSelect from "../select/OptSelect.vue";
import NormalNumberInput from "../common/NormalNumberInput.vue";
import DateRange from "../common/DateRange.vue";
import CommonUploader from "~/components/optimaztion/uploader/CommonUploader.vue";


const $q = useQuasar()

const props = defineProps({
  data: {type: Array, default: [{}]},
  modelValue: {type: Array, default: []},
  readOnly: {type: Boolean, default: false},
  userList: {type: Array, default: []},
  userDict: {type: Object, default: {}},
  columns: {type: Array, default: []},
  systemList: {type: Array, default: []},
  systemDict: {type: Object, default: {}},
  canSearch: {type: Boolean, default: true},
  canImport: {type: Boolean, default: false},
  canExport: {type: Boolean, default: true},
  canAdd: {type: Boolean, default: false},
  canDelete: {type: Boolean, default: false},
  canDownloadTemplate: {type: Boolean, default: false},
  title: {type: String, default: ''},
  exportName: {type: String, default: date.formatDate(Date.now(), 'YYYYMMDDHHmmss')},
  fullScreen: {type: Boolean, default: false},
  uploadLimit: {
    type: Object,
    default: {
      accept: ".xlsx,.xls",
      maxFileSize: "1024*1024*100"
    }
  },
  uploadParams: {
    type: Object,
    default: {}
  },
})

const emit = defineEmits([
  'update:data', 'deleteRow', 'deleteSelected', 'resetRow', 'addRow', 'copyRow', 'saveData', 'createData', 'updateData',
  'fileAdded', 'beforeShow', 'downloadTemplate', 'beforeHide', 'editRow', 'cellClick'
])

const bus = inject('bus')

onMounted(() => {
})

/*
*  +----------------------------+
*  +         datas              +
*  +----------------------------+
*/
const importFile = ref(null)

const search_text = ref('')

const selected = ref([])

const tableStyle = ref('max-height: 100vh;')

const test = () => {
}
// table 的 ref, 可以通过 table.value.func() 调用 table 组件的各种方法
const table = ref(null)


const initial_page_config = ref({
  sortBy: 'id',
  descending: false,
  page: 1,
  rowsPerPage: 10
})

/*
*  +----------------------------+
*  +         computed             +
*  +----------------------------+
*/
const search_text_trim = computed(() => search_text.value.trim())
const rows_per_page_options = computed(() => {
  return [10, 20, 0]
})

const rows = computed({
  get() {
    if (props.data.length === 0) {
      emit('update:data', [{}])
      return props.data
    }
    return props.data
  },
  set(val) {
    emit('update:data', val)
  }
})


/*
*  +----------------------------+
*  +         methods            +
*  +----------------------------+
*/
const cellClick = (props) => {
  emit('cellClick', props)
}

const dateRangeStartTime = (props) => {
  return props.col.date_options.hasOwnProperty('start_time') ? props.col.date_options.start_time : props.row[props.col.date_options.start_time_field]
}

const dateRangeEndTime = (props) => {
  return props.col.date_options.hasOwnProperty('end_time') ? props.col.date_options.end_time : props.row[props.col.date_options.end_time_field]
}

const cellDisable = ({row, col}) => {
  if (!col.hasOwnProperty('disableFunction')) return false
  return col.disableFunction(row)
}

const beforeShow = (props) => {
  emit('beforeShow', props)
}

const beforeHide = (props) => {
  emit('beforeHide', props)
}

const toggleFullscreen = (p) => {
  if (p.inFullscreen) {
    tableStyle.value = 'max-height: 60vh;'
  } else {
    tableStyle.value = 'max-height: 100vh;'
  }
  p.toggleFullscreen()
}

const downloadTemplate = () => {
  emit('downloadTemplate')
}

const deleteRow = (index) => {
  emit('deleteRow', index)
}

const deleteSelected = () => {
  if (selected.value.length == 0) {
    $q.notify({
      type: 'warning',
      message: '没有选中任何数据, 请选择数据后操作',
      timeout: 2000
    })
    return
  }
  emit('deleteSelected', selected.value)
}

const resetRow = (index) => {
  emit('resetRow', index)
}

const addRow = () => {
  emit('addRow')
}

const editRow = (row) => {
  emit('editRow', row)
}

const copyRow = (row) => {
  emit('copyRow', row)
}

const saveData = debounce((props) => {
  emit('saveData', props)
}, 100)

const createData = (row) => {
  emit('createData', row)
}
const updateData = (row) => {
  emit('updateData', row)
}

const wrapCsvValue = (val, formatFn, row) => {
  let formatted = formatFn !== void 0
      ? formatFn(val, row)
      : val

  formatted = formatted === void 0 || formatted === null
      ? ''
      : String(formatted)

  formatted = formatted.split('"').join('""')
  /**
   * Excel accepts \n and \r in strings, but some other CSV parsers do not
   * Uncomment the next two lines to escape new lines
   */
  // .split('\n').join('\\n')
  // .split('\r').join('\\r')

  return `"${formatted}"`
}

const exportTable = (datas) => {
  if (datas.length == 0) {
    $q.notify({
      type: 'warning',
      message: '没有选中任何数据, 请选择数据后操作',
      timeout: 2000
    })
    return
  }
  // naive encoding to csv format
  const content = [props.columns.filter(c => c.type != 'buttons').map(col => wrapCsvValue(col.label))].concat(
      datas.map(row => props.columns.map(col => wrapCsvValue(
          typeof col.field === 'function'
              ? col.field(row)
              : row[col.field === void 0 ? col.name : col.field],
          col.format,
          row
      )).join(','))
  ).join('\r\n')
  const status = exportFile(
      props.exportName + '.csv',
      content,
      {
        encoding: 'utf-8', byteOrderMark: '\uFEFF',
        mimeType: 'text/csv;charset=UTF-8;'
      }
  )

  if (status !== true) {
    $q.notify({
      message: '文件导出失败',
      color: 'negative',
      icon: 'warning',
      position: 'top'
    })
  }
}

const importFileData = ref([])


const importTable = () => {
  importFile.value.pickFiles()
}

const fileAdded = () => {
  emit('fileAdded', importFileData)
}

const clearSelection = () => {
  table.value.clearSelection()
}

const lastPage = () => {
  table.value.lastPage()
}

const scrollTo = (index) => {
  table.value.scrollTo(index)
}


/*
*  +----------------------------+
*  +         总线             +
*  +----------------------------+
*/
// 删除成功后, 清楚选中的行数据
bus.on('clearSelection', () => {
  table.value.clearSelection()
})


defineExpose({
  clearSelection,
  lastPage,
  scrollTo,
  selected
})


</script>

<style scoped>

</style>